#include "math.h"
#include <vector>

#include "Directorio.h"
#include "funciones.h"
#include "Cuba.h"

namespace Toolkit
{

Directorio::Directorio(const unsigned long int &posicionPrimerBloque, const string &ruta,
		const string &nombre)
{
	this->profundidad = 0;

	stringstream metadata;
	serializacionDigito(metadata, BINARIO, profundidad);

	this->almacenamiento = new Archivo(sizeof(unsigned long int), ruta,
									   string(nombre).append(".directorio"),
									   BINARIO, metadata);
	directorio.push_back(posicionPrimerBloque);
}

Directorio::Directorio(const string &ruta, const string &nombre)
{
	this->almacenamiento = new Archivo(ruta, string(nombre).append(".directorio"),
									   BINARIO);
	this->recuperar();
}

Directorio::~Directorio()
{
	persistir();

	if (almacenamiento != NULL)
	{
		delete almacenamiento;
	}
}

unsigned int Directorio::getCantidadElementos() const
{
	return (unsigned int)pow(2, profundidad);
}

bool Directorio::puedoContraer() const
{
    /**
     * Recorro el directorio y verifico que todas las cubas
     * esten direccionadas por lo menos por dos direcciones.
     */
    for (unsigned int i = 0; i < getCantidadElementos(); i += 2)
    {
        if (directorio[i] != directorio[i + 1])
        {
            return false;
        }
    }

    return true;
}

bool Directorio::intentarContraer()
{
	if (!puedoContraer())
	{
		return false;
	}

    for (unsigned int i = 0; i < getCantidadElementos() / 2; i++)
    {
        directorio[i] = directorio[2 * i];
    }

    directorio.resize(getCantidadElementos() / 2);
    profundidad--;

    return true;
}

void Directorio::expandir()
{
    profundidad++;
    unsigned int cantidadElementos = getCantidadElementos();
    directorio.resize(cantidadElementos);

    int j = 0;
    for (unsigned int i = cantidadElementos / 2; i > 0; i--)
    {
        directorio[cantidadElementos - 1 - j] = directorio[i - 1];
        directorio[cantidadElementos - 2 - j] = directorio[i - 1];
        j += 2;
    }
}

void Directorio::persistir()
{
	almacenamiento->borrarContenido();
	persistirProfundidad();

    for (unsigned int i = 0; i < this->directorio.size(); i++)
    {
        stringstream posicionSerializada;
        serializacionDigito(posicionSerializada, BINARIO, directorio[i]);
        this->almacenamiento->guardarDato(posicionSerializada);
    }
}

void Directorio::recuperar()
{
	recuperarProfundidad();

	this->directorio.resize(almacenamiento->cantElementos());

    for (unsigned int i = 0; i < almacenamiento->cantElementos(); i++)
    {
        stringstream posicionSerializada;

        this->almacenamiento->recuperarDato(i, posicionSerializada);
        hidratacionDigito(posicionSerializada, BINARIO, directorio[i]);
    }
}

unsigned long int Directorio::obtenerPosicionCuba(long int direccionDirectorio) const
{
	return directorio[direccionDirectorio];
}

bool Directorio::buscarCompaneroCuba(const Cuba &cuba,
        long int &posicionCompanero) const
{
    if (!tieneCompanero(cuba))
    {
        posicionCompanero = 0;
        return false;
    }

    /**
     * Aplico un xor a la direccion de la cuba, modificando el ultimo bit de esta,
     * obteniendo asi la posicion de la cuba Companera.
     */
    posicionCompanero = posicionCompanero ^ 1;

    return true;
}

bool Directorio::tieneCompanero(const Cuba &cuba) const
{
    return ((cuba.getProfundidad() != 0) && (cuba.getProfundidad() == profundidad));
}

void Directorio::agregar(const unsigned long int &dato, const long int &posicionDirectorio)
{
	directorio[posicionDirectorio] = dato;
}

void Directorio::recuperarProfundidad()
{
	stringstream metadata;
	almacenamiento->leerMetaData(metadata);
	hidratacionDigito(metadata, BINARIO, profundidad);
}

void Directorio::persistirProfundidad()
{
	stringstream metadata;
	serializacionDigito(metadata, BINARIO, profundidad);
	almacenamiento->actualizarMetadata(metadata);
}

unsigned long int Directorio::getProfundidad() const
{
	return profundidad;
}

void Directorio::actualizar(const unsigned long int posicionNueva, const unsigned long int inicio,
					const unsigned long int fin)
{
	for (unsigned int i = inicio; i < fin; ++i)
	{
		directorio[i] = posicionNueva;
	}
}

unsigned int Directorio::cantidadRegistros(const Cuba &cuba) const
{
	int diferenciaProfundidades = getProfundidad() - cuba.getProfundidad();

	return ((unsigned int)pow(2, diferenciaProfundidades));
}

}
